﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace game
{
    public class PointSpriteParticleSystem : ParticleSystem
    {
        protected PointSpriteParticleVertex[] vertices;

        protected EffectParameter ep_gViewportHeight;

        #region Effect

        protected override void SetEffectParamHandles()
        {
            ep_gVP = effect.Parameters["gVP"];
            ep_gTex = effect.Parameters["gTex"];
            ep_gEyePosW = effect.Parameters["gEyePosW"];
            ep_gAccel = effect.Parameters["gAccel"];
            ep_gPSystemTime = effect.Parameters["gPSystemTime"];
            ep_gViewportHeight = effect.Parameters["gViewportHeight"];
        }

        protected override void SetEffectParams()
        {
            Camera camera = scene.ActiveCamera;
            ep_gVP.SetValue(camera.View * camera.Projection);
            ep_gEyePosW.SetValue(camera.Position);
            ep_gPSystemTime.SetValue(elapsedSystemTimeMs);
            ep_gViewportHeight.SetValue(
                ShooterGame.Instance.GraphicsDevice.Viewport.Height);
            ep_gAccel.SetValue(Acceleration);
            ep_gTex.SetValue(texture);
        }

        #endregion

        #region ctor

        public PointSpriteParticleSystem(Scene scene, Vector3 position)
            : base(scene, position)
        {
        }

        #endregion

        #region Init

        protected override void InitParameters()
        {
            textureName = "Textures\\Explosions\\expl2";
            effectName = "Effects\\RegularPSystem";
            effectTechName = "AdditiveBlendingSystem";

            Acceleration = 0.0f;
            maxParticles = 100;

            emitter = null;

            MinInitialSpeed = 0.0f;
            MaxInitialSpeed = 0.0f;
            MinInitialSize = 50;
            MaxInitialSize = 100;
            MinLifeTimeMs = 1000;
            MaxLifeTimeMs = 5000;

            color = Color.White;
        }

        protected override void InitParticle(int index, Vector3 position, float initialTimeMsOffset)
        {
            vertices[index].InitialTimeMs = elapsedSystemTimeMs + initialTimeMsOffset;
            vertices[index].InitialSize
                = RandomHelper.RandomBetween(MinInitialSize, MaxInitialSize);
            vertices[index].DieLifeTimeMs
                = RandomHelper.RandomBetween(MinLifeTimeMs, MaxLifeTimeMs);
            vertices[index].InitialColor = color;
        }

        protected override void DeinitPaticle(int index)
        {
            vertices[index].DieLifeTimeMs = 0;
        }


        public override void Initialize()
        {
            vertices = new PointSpriteParticleVertex[maxParticles];

            for (int i = 0; i < vertices.Length; i++)
            {
                DeinitPaticle(i);
            }

            texture = ShooterGame.Instance.Content.Load<Texture>(textureName);
            effect = ShooterGame.Instance.Content.Load<Effect>(effectName);
            effectTechnique = effect.Techniques[effectTechName];

            SetEffectParamHandles();
        }

        #endregion

        #region Adding particles

        public override int AddParticles(int count)
        {
            int a = count;
            while (count > 0)
            {
                if (firstFreeParticleIndex == firstActiveParticleIndex)
                {
                    break;
                }

                InitParticle(firstFreeParticleIndex, Vector3.Zero, 0);
                if (firstActiveParticleIndex == -1)
                {
                    firstActiveParticleIndex = firstFreeParticleIndex;
                }
                firstFreeParticleIndex = (firstFreeParticleIndex + 1) % maxParticles;
                count--;
            }
            return count;
        }


        public override
            bool AddParticle(Vector3 position, float initialTimeMsOffset)
        {
            if (firstFreeParticleIndex == firstActiveParticleIndex)
            {
                return false;
            }

            InitParticle(firstFreeParticleIndex, position, initialTimeMsOffset);
            if (firstActiveParticleIndex == -1)
            {
                firstActiveParticleIndex = firstFreeParticleIndex;
            }
            firstFreeParticleIndex = (firstFreeParticleIndex + 1) % maxParticles;

            return true;
        }

        public override void SetParticles(int n)
        {
            if (n > maxParticles)
                return;

            elapsedSystemTimeMs = 0;
            firstFreeParticleIndex = firstActiveParticleIndex = 0;
            for (int i = 0; i < n; i++)
            {
                InitParticle(i, Vector3.Zero, 0);
            }
            for (int i = n; i < maxParticles; i++)
            {
                DeinitPaticle(i);
            }
        }

        #endregion

        #region Update Draw


        public override void Update(GameTime gameTime)
        {
            elapsedSystemTimeMs += gameTime.ElapsedGameTime.Milliseconds;
            if (firstActiveParticleIndex != -1)
            {
                int i = 0;
                while (true)
                {
                    i++;
                    if (i > maxParticles)
                    {
                        firstActiveParticleIndex = -1;
                        break;
                    }

                    if (elapsedSystemTimeMs - vertices[firstActiveParticleIndex].InitialTimeMs
                        > vertices[firstActiveParticleIndex].DieLifeTimeMs)
                    {
                        firstActiveParticleIndex = (firstActiveParticleIndex + 1) % maxParticles;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            if (emitter != null)
                emitter.Update(gameTime, position, rotation);
        }

        public override void Draw(GameTime gameTime)
        {
            if (firstActiveParticleIndex == -1)
                return;

            SetEffectParams();

            ShooterGame.Instance.GraphicsDevice.RenderState.PointSizeMax = 256.0f;

            ShooterGame.Instance.GraphicsDevice.VertexDeclaration
                    = PointSpriteParticleVertex.VertexDecl;

            effect.CurrentTechnique = effectTechnique;

            effect.Begin();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();

                if (firstFreeParticleIndex > firstActiveParticleIndex)
                {
                    ShooterGame.Instance.GraphicsDevice.DrawUserPrimitives(PrimitiveType.PointList,
                        vertices, firstActiveParticleIndex, firstFreeParticleIndex - firstActiveParticleIndex);
                }
                else
                {
                    if (firstFreeParticleIndex > 0)
                    {
                        ShooterGame.Instance.GraphicsDevice.DrawUserPrimitives(PrimitiveType.PointList,
                            vertices, 0, firstFreeParticleIndex);
                    }

                    ShooterGame.Instance.GraphicsDevice.DrawUserPrimitives(PrimitiveType.PointList,
                        vertices, firstActiveParticleIndex, maxParticles - firstActiveParticleIndex);
                }

                pass.End();
            }
            effect.End();
        }

        #endregion
    }
}
